package com.leyou.item.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.leyou.common.dto.PageDTO;
import com.leyou.common.exception.LyException;
import com.leyou.common.utils.JsonUtils;
import com.leyou.item.dto.*;
import com.leyou.item.entity.Sku;
import com.leyou.item.entity.Spu;
import com.leyou.item.entity.SpuDetail;
import com.leyou.item.service.SkuService;
import com.leyou.item.mapper.SpuMapper;
import com.leyou.item.service.*;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class SpuServiceImpl extends ServiceImpl<SpuMapper, Spu> implements SpuService {
    @Autowired
    private BrandService brandService;

    @Autowired
    private CategoryService categoryService;


    @Autowired
    private SpecParamService paramService;

    @Autowired
    private SpuDetailService detailService;

    @Autowired
    private SkuService skuService;

    @Override
    public PageDTO<SpuDTO> pageQuery(Integer page, Integer rows, Boolean saleable, Long brandId, Long categoryId, Long id) {

        IPage<Spu> iPage = new Page<>(page, rows);

        //select * from tb_spu [where saleable = xx ,] limit x,y
        page(iPage, new QueryWrapper<Spu>()
                .eq(null != saleable, "saleable", saleable)
                .eq(null != brandId, "brand_id", brandId)
                .eq(null != categoryId, "cid3", categoryId)
                .eq(null != id, "id", id)
        );

        List<SpuDTO> spuDTOS = SpuDTO.convertEntityList(iPage.getRecords());

        spuDTOS.forEach(spuDTO -> {

            //根据分类id的集合查询对应的分类集合，并取出分类名称并拼接为字符串
            String names = this.categoryService
                    .listCategoryByIds(spuDTO.getCategoryIds())
                    .stream()
                    .map(CategoryDTO::getName)
                    .collect(Collectors.joining("/"));

            //TODO 查询要考虑缓存，缓存击穿

            spuDTO.setCategoryName(names);
            //根据brandId查询对应的品牌
            spuDTO.setBrandName(this.brandService.getById(spuDTO.getBrandId()).getName());
        });

        //封装spu的分页查询结果
        return new PageDTO<>(iPage.getTotal(), iPage.getPages(), spuDTOS);
    }

    @Override
    public List<SpecParamDTO> listSpec(Long spuId, Boolean searching) {

        //spuId获取到对应的规格参数，以及规格参数值，规格参数组
        ;

        //查询spu获取分类id
        Long cid = getById(spuId).getCid3();

        //跟分类id查询当前分类下所有的规格参数组


        //普通，遍历规格参数组，根据每个组id查询对应的组内参数

        //跟分类id查询当前分类下所有的规格参数
        List<SpecParamDTO> specParamDTOS = SpecParamDTO
                .convertEntityList(this.paramService
                        .query()
                        .eq("category_id", cid)
                        .eq(null != searching, "searching", searching)
                        .list());

        //处理每一个specParam的value，？？在spuDetail中


        //对规格参数集合，进行分组，已groupId分组，同id，为一组,key为groupId，value同id一组集合


        //获取规格参数的value，
        SpuDetail spuDetail = this.detailService.getById(spuId);

        //json,class

        //nativeRead 第一个参数，接送，第二个参数，类型限定
        //key 规格参数的id，value，规格参数的值
        Map<Long, Object> valueMap = JsonUtils.nativeRead(spuDetail.getSpecification(), new TypeReference<Map<Long, Object>>() {
        });

        //遍历specParamDTO，给每个value赋值，每个value的值，就是valuemap的value
        specParamDTOS.forEach(specParamDTO -> {

            //规格参数的id
            Long key = specParamDTO.getId();

            specParamDTO.setValue(valueMap.get(key));
        });

        return specParamDTOS;
    }

    @Override
    @Transactional
    public void addGoods(SpuDTO spuDTO) {

        if (CollectionUtils.isEmpty(spuDTO.getSkus())) {
            throw new LyException(400, "每个spu下至少需要一个sku");
        }

        Spu spu = spuDTO.toEntity(Spu.class);

        //保存spu同时主键回显
        this.save(spu);

        SpuDetail spuDetail = spuDTO.getSpuDetail().toEntity(SpuDetail.class);

        spuDetail.setSpuId(spu.getId());

        //保存spuDetail
        this.detailService.save(spuDetail);


        //从skuDTO转换到sku的集合同时给属性，spuId以及saleable赋值
        List<Sku> skus = spuDTO.getSkus().stream().map(skuDTO -> {
            Sku sku = skuDTO.toEntity(Sku.class);
            sku.setSpuId(spu.getId());
            sku.setSaleable(true);
            return sku;
        }).collect(Collectors.toList());

        //sku的批量保存
        this.skuService.saveBatch(skus);

    }

    @Override
    public SpuDTO querySpuById(Long spuId) {
        return new SpuDTO(this.getById(spuId));
    }

    @Override
    public SpuDTO queryGoods(Long spuId) {

        List<SpuDTO> spuDTOS = this.pageQuery(1, 1, null, null, null, spuId).getItems();

        if (CollectionUtils.isEmpty(spuDTOS)) {
            throw new LyException(204, "对应商品不存在");
        }
        SpuDTO spuDTO = spuDTOS.get(0);

        spuDTO.setSkus(this.skuService.listSkuBySpu(spuId));
        spuDTO.setSpuDetail(this.detailService.querySpuDetailById(spuId));


        return spuDTO;
    }

    @Autowired
    private AmqpTemplate amqptemplate;


    @Override
    @Transactional
    public void modifySaleable(Long id, Boolean saleable) {

        Spu spu = new Spu();
        spu.setId(id);
        spu.setSaleable(saleable);
        spu.setUpdateTime(new Date());

        //update tb_spu set saleable = false ,update_time = now() where id = #{id}
        this.updateById(spu);

        Sku sku = new Sku();
        sku.setSaleable(saleable);
        sku.setUpdateTime(spu.getUpdateTime());

        //update tb_sku set saleable = false,update_time = now where spu_id = id
        //update的api中两个参数，新的sku，以及查询条件
        this.skuService.update(sku, new QueryWrapper<Sku>().eq("spu_id", id));
        //mq
        String routingKey = saleable ? "item.up" : "item.down";
        //向指定交换机发送消息，消息内容为spuId
        this.amqptemplate.convertAndSend("leyou.item", routingKey, id);
    }

    @Override
    public void updateGoods(SpuDTO spuDTO) {

        //spuId不为空说明要修改spu
        if (spuDTO.getId() != null) {
            Spu spu = spuDTO.toEntity(Spu.class);
            spu.setUpdateTime(new Date());
            this.updateById(spu);
        }

        SpuDetailDTO spuDetailDTO = spuDTO.getSpuDetail();
        //spuDetail不为空则表示要修改spuDetail
        if (null != spuDetailDTO) {
            SpuDetail spuDetail = spuDetailDTO.toEntity(SpuDetail.class);
            this.detailService.updateById(spuDetail);
        }


        List<SkuDTO> skuDTOS = spuDTO.getSkus();
        if (!CollectionUtils.isEmpty(skuDTOS)) {
            //要修改sku，是普通修改，还是删除，新增

            List<Sku> skuList = skuDTOS.stream().map(skuDTO -> skuDTO.toEntity(Sku.class)).collect(Collectors.toList());

            //map中最多两组，当key为true，表示saleable就是null，即表示不删除
            Map<Boolean, List<Sku>> skuMap =
                    skuList
                            .stream()
                            .collect(Collectors.groupingBy(sku -> sku.getSaleable() == null));


            //获取要删除的sku的集合
            List<Sku> skusToBeDelete = skuMap.get(false);

            if (!CollectionUtils.isEmpty(skusToBeDelete)) {
                //根据sku的id集合删除对应的sku信息
                this.skuService.removeByIds(skusToBeDelete.stream().map(Sku::getId).collect(Collectors.toList()));

            }
            List<Sku> skusToBeAddOrUpdate = skuMap.get(true);

            if (!CollectionUtils.isEmpty(skusToBeAddOrUpdate)) {
                //批量修改或新增
                this.skuService.saveOrUpdateBatch(skusToBeAddOrUpdate);

            }

        }
    }
}
